using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Json;
using Lzfy_His_Service.Models;
using Lzfy_His_Service.Models.Configuration;
using Lzfy_His_Service.Logging;

namespace Lzfy_His_Service.Services
{
    /// <summary>
    /// Senparc缓存服务实现
    /// </summary>
    public class SenparcCacheService : ISenparcCacheService
    {
        private readonly IMemoryCache _memoryCache;
        private readonly IDistributedCache _distributedCache;
        private readonly ILogger<SenparcCacheService> _logger;
        private readonly SenparcWeChatConfiguration _config;
        
        // 缓存键前缀
        private const string ACCESS_TOKEN_KEY = "senparc:wechat:access_token";
        private const string JSAPI_TICKET_KEY = "senparc:wechat:jsapi_ticket";
        private const string USER_INFO_KEY_PREFIX = "senparc:wechat:user_info:";
        private const string CACHE_NAMESPACE = "Lzfy_Refund_Cache";

        public SenparcCacheService(
            IMemoryCache memoryCache,
            IDistributedCache distributedCache,
            ILogger<SenparcCacheService> logger,
            IOptions<SenparcWeChatConfiguration> config)
        {
            _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
            _distributedCache = distributedCache ?? throw new ArgumentNullException(nameof(distributedCache));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _config = config.Value ?? throw new ArgumentNullException(nameof(config));
        }

        /// <summary>
        /// 获取AccessToken
        /// </summary>
        /// <returns>AccessToken</returns>
        public async Task<string> GetAccessTokenAsync()
        {
            try
            {
                var cacheKey = GetCacheKey(ACCESS_TOKEN_KEY);
                
                // 先从内存缓存获取
                if (_memoryCache.TryGetValue(cacheKey, out string? memoryToken) && !string.IsNullOrEmpty(memoryToken))
                {
                    _logger.LogDebug("从内存缓存获取AccessToken成功");
                    return memoryToken;
                }

                // 从分布式缓存获取
                var distributedToken = await _distributedCache.GetStringAsync(cacheKey);
                if (!string.IsNullOrEmpty(distributedToken))
                {
                    // 同步到内存缓存
                    var memoryExpiry = TimeSpan.FromSeconds(Math.Min(_config.Cache.AccessTokenCacheSeconds, 300)); // 内存缓存最多5分钟
                    _memoryCache.Set(cacheKey, distributedToken, memoryExpiry);
                    
                    _logger.LogDebug("从分布式缓存获取AccessToken成功并同步到内存缓存");
                    return distributedToken;
                }

                _logger.LogDebug("AccessToken缓存未命中");
                return string.Empty;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取AccessToken缓存失败");
                return string.Empty;
            }
        }

        /// <summary>
        /// 设置AccessToken
        /// </summary>
        /// <param name="token">令牌</param>
        /// <param name="expiry">过期时间</param>
        public async Task SetAccessTokenAsync(string token, TimeSpan expiry)
        {
            if (string.IsNullOrEmpty(token))
            {
                throw new ArgumentException("AccessToken不能为空", nameof(token));
            }

            try
            {
                var cacheKey = GetCacheKey(ACCESS_TOKEN_KEY);
                
                // 设置分布式缓存
                var distributedOptions = new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = expiry
                };
                await _distributedCache.SetStringAsync(cacheKey, token, distributedOptions);

                // 设置内存缓存（较短的过期时间）
                var memoryExpiry = TimeSpan.FromSeconds(Math.Min(expiry.TotalSeconds, 300)); // 内存缓存最多5分钟
                _memoryCache.Set(cacheKey, token, memoryExpiry);

                _logger.LogInformation("设置AccessToken缓存成功，过期时间：{Expiry}", expiry);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "设置AccessToken缓存失败");
                throw;
            }
        }

        /// <summary>
        /// 获取JsApiTicket
        /// </summary>
        /// <returns>JsApiTicket</returns>
        public async Task<string> GetJsApiTicketAsync()
        {
            try
            {
                var cacheKey = GetCacheKey(JSAPI_TICKET_KEY);
                
                // 先从内存缓存获取
                if (_memoryCache.TryGetValue(cacheKey, out string? memoryTicket) && !string.IsNullOrEmpty(memoryTicket))
                {
                    _logger.LogDebug("从内存缓存获取JsApiTicket成功");
                    return memoryTicket;
                }

                // 从分布式缓存获取
                var distributedTicket = await _distributedCache.GetStringAsync(cacheKey);
                if (!string.IsNullOrEmpty(distributedTicket))
                {
                    // 同步到内存缓存
                    var memoryExpiry = TimeSpan.FromSeconds(Math.Min(_config.Cache.JsApiTicketCacheSeconds, 300));
                    _memoryCache.Set(cacheKey, distributedTicket, memoryExpiry);
                    
                    _logger.LogDebug("从分布式缓存获取JsApiTicket成功并同步到内存缓存");
                    return distributedTicket;
                }

                _logger.LogDebug("JsApiTicket缓存未命中");
                return string.Empty;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取JsApiTicket缓存失败");
                return string.Empty;
            }
        }

        /// <summary>
        /// 设置JsApiTicket
        /// </summary>
        /// <param name="ticket">票据</param>
        /// <param name="expiry">过期时间</param>
        public async Task SetJsApiTicketAsync(string ticket, TimeSpan expiry)
        {
            if (string.IsNullOrEmpty(ticket))
            {
                throw new ArgumentException("JsApiTicket不能为空", nameof(ticket));
            }

            try
            {
                var cacheKey = GetCacheKey(JSAPI_TICKET_KEY);
                
                // 设置分布式缓存
                var distributedOptions = new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = expiry
                };
                await _distributedCache.SetStringAsync(cacheKey, ticket, distributedOptions);

                // 设置内存缓存
                var memoryExpiry = TimeSpan.FromSeconds(Math.Min(expiry.TotalSeconds, 300));
                _memoryCache.Set(cacheKey, ticket, memoryExpiry);

                _logger.LogInformation("设置JsApiTicket缓存成功，过期时间：{Expiry}", expiry);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "设置JsApiTicket缓存失败");
                throw;
            }
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns>用户信息</returns>
        public async Task<WeChatUserInfoResponse?> GetUserInfoAsync(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentException("用户ID不能为空", nameof(userId));
            }

            try
            {
                var cacheKey = GetCacheKey($"{USER_INFO_KEY_PREFIX}{userId}");
                
                // 先从内存缓存获取
                if (_memoryCache.TryGetValue(cacheKey, out WeChatUserInfoResponse? memoryUserInfo) && memoryUserInfo != null)
                {
                    _logger.LogDebug("从内存缓存获取用户信息成功，用户ID：{UserId}", userId);
                    return memoryUserInfo;
                }

                // 从分布式缓存获取
                var distributedUserInfoJson = await _distributedCache.GetStringAsync(cacheKey);
                if (!string.IsNullOrEmpty(distributedUserInfoJson))
                {
                    var distributedUserInfo = JsonSerializer.Deserialize<WeChatUserInfoResponse>(distributedUserInfoJson);
                    if (distributedUserInfo != null)
                    {
                        // 同步到内存缓存
                        var memoryExpiry = TimeSpan.FromSeconds(Math.Min(_config.Cache.UserInfoCacheSeconds, 600)); // 内存缓存最多10分钟
                        _memoryCache.Set(cacheKey, distributedUserInfo, memoryExpiry);
                        
                        _logger.LogDebug("从分布式缓存获取用户信息成功并同步到内存缓存，用户ID：{UserId}", userId);
                        return distributedUserInfo;
                    }
                }

                _logger.LogDebug("用户信息缓存未命中，用户ID：{UserId}", userId);
                return null;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取用户信息缓存失败，用户ID：{UserId}", userId);
                return null;
            }
        }

        /// <summary>
        /// 设置用户信息
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="userInfo">用户信息</param>
        /// <param name="expiry">过期时间</param>
        public async Task SetUserInfoAsync(string userId, WeChatUserInfoResponse userInfo, TimeSpan expiry)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentException("用户ID不能为空", nameof(userId));
            }

            if (userInfo == null)
            {
                throw new ArgumentNullException(nameof(userInfo));
            }

            try
            {
                var cacheKey = GetCacheKey($"{USER_INFO_KEY_PREFIX}{userId}");
                
                // 设置分布式缓存
                var userInfoJson = JsonSerializer.Serialize(userInfo);
                var distributedOptions = new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = expiry
                };
                await _distributedCache.SetStringAsync(cacheKey, userInfoJson, distributedOptions);

                // 设置内存缓存
                var memoryExpiry = TimeSpan.FromSeconds(Math.Min(expiry.TotalSeconds, 600)); // 内存缓存最多10分钟
                _memoryCache.Set(cacheKey, userInfo, memoryExpiry);

                _logger.LogInformation("设置用户信息缓存成功，用户ID：{UserId}，过期时间：{Expiry}", userId, expiry);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "设置用户信息缓存失败，用户ID：{UserId}", userId);
                throw;
            }
        }

        /// <summary>
        /// 清除缓存
        /// </summary>
        /// <param name="pattern">缓存键模式</param>
        public async Task ClearCacheAsync(string pattern)
        {
            try
            {
                if (string.IsNullOrEmpty(pattern))
                {
                    // 清除所有Senparc相关缓存
                    await ClearAllSenparcCacheAsync();
                }
                else
                {
                    // 根据模式清除特定缓存
                    await ClearCacheByPatternAsync(pattern);
                }

                _logger.LogInformation("清除缓存成功，模式：{Pattern}", pattern ?? "全部");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "清除缓存失败，模式：{Pattern}", pattern);
                throw;
            }
        }

        #region 私有方法

        /// <summary>
        /// 获取缓存键
        /// </summary>
        /// <param name="key">原始键</param>
        /// <returns>带命名空间的缓存键</returns>
        private static string GetCacheKey(string key)
        {
            return $"{CACHE_NAMESPACE}:{key}";
        }

        /// <summary>
        /// 清除所有Senparc相关缓存
        /// </summary>
        private async Task ClearAllSenparcCacheAsync()
        {
            // 清除内存缓存中的特定键
            var keysToRemove = new[]
            {
                GetCacheKey(ACCESS_TOKEN_KEY),
                GetCacheKey(JSAPI_TICKET_KEY)
            };

            foreach (var key in keysToRemove)
            {
                _memoryCache.Remove(key);
            }

            // 注意：IDistributedCache接口没有提供批量删除或按模式删除的方法
            // 在实际项目中，如果使用Redis，可以通过IDatabase接口进行模式删除
            // 这里只能删除已知的键
            await _distributedCache.RemoveAsync(GetCacheKey(ACCESS_TOKEN_KEY));
            await _distributedCache.RemoveAsync(GetCacheKey(JSAPI_TICKET_KEY));

            _logger.LogInformation("清除所有Senparc缓存完成");
        }

        /// <summary>
        /// 根据模式清除缓存
        /// </summary>
        /// <param name="pattern">模式</param>
        private async Task ClearCacheByPatternAsync(string pattern)
        {
            var cacheKey = GetCacheKey(pattern);
            
            // 从内存缓存移除
            _memoryCache.Remove(cacheKey);
            
            // 从分布式缓存移除
            await _distributedCache.RemoveAsync(cacheKey);

            _logger.LogDebug("清除缓存完成，键：{CacheKey}", cacheKey);
        }

        #endregion
    }

    /// <summary>
    /// Senparc缓存服务扩展
    /// </summary>
    public static class SenparcCacheServiceExtensions
    {
        /// <summary>
        /// 清除AccessToken缓存
        /// </summary>
        /// <param name="cacheService">缓存服务</param>
        public static async Task ClearAccessTokenCacheAsync(this ISenparcCacheService cacheService)
        {
            await cacheService.ClearCacheAsync("senparc:wechat:access_token");
        }

        /// <summary>
        /// 清除JsApiTicket缓存
        /// </summary>
        /// <param name="cacheService">缓存服务</param>
        public static async Task ClearJsApiTicketCacheAsync(this ISenparcCacheService cacheService)
        {
            await cacheService.ClearCacheAsync("senparc:wechat:jsapi_ticket");
        }

        /// <summary>
        /// 清除用户信息缓存
        /// </summary>
        /// <param name="cacheService">缓存服务</param>
        /// <param name="userId">用户ID</param>
        public static async Task ClearUserInfoCacheAsync(this ISenparcCacheService cacheService, string userId)
        {
            await cacheService.ClearCacheAsync($"senparc:wechat:user_info:{userId}");
        }

        /// <summary>
        /// 预热缓存
        /// </summary>
        /// <param name="cacheService">缓存服务</param>
        /// <param name="senparcWeChatService">企业微信服务</param>
        public static async Task WarmupCacheAsync(this ISenparcCacheService cacheService, ISenparcWeChatService senparcWeChatService)
        {
            try
            {
                // 预热AccessToken
                var accessToken = await cacheService.GetAccessTokenAsync();
                if (string.IsNullOrEmpty(accessToken))
                {
                    // 如果缓存中没有，触发获取新的AccessToken
                    // 注意：这里需要通过其他方式获取，因为ISenparcWeChatService接口中没有直接的GetAccessToken方法
                    // 可以通过调用其他需要AccessToken的方法来间接触发获取
                }

                // 预热JsApiTicket
                var jsApiTicket = await cacheService.GetJsApiTicketAsync();
                if (string.IsNullOrEmpty(jsApiTicket))
                {
                    // 同样，通过调用需要JsApiTicket的方法来间接触发获取
                    await senparcWeChatService.GetJsApiConfigAsync("https://example.com");
                }
            }
            catch (Exception ex)
            {
                // 预热失败不应该影响应用启动
                // 这里可以记录日志，但不抛出异常
            }
        }
    }
}